Repository navigation
Conversation
|
Updated 6:12 AM PT - Oct 22nd, 2026
❌ @robobun, your commit 12b6213 has 1 failures in
🧪 To try this PR locally: bunx bun-pr 46238That installs a local version of the PR into your bun-46238 --bun |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
WalkthroughReused packages now refresh lifecycle-script metadata under the conditions described in the package manager. New tests cover tarball, Git, and GitHub dependency updates, including metadata preservation and script execution. ChangesInstall lifecycle-script metadata
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to
|
|
Status How it was reproduced: a debug build of main (f2de590) runs the tests of this PR with
With this PR the file passes: 194 pass, 0 fail. PR: #46238 |
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟣
src/install/PackageManager/processDependencyList.rs— pre-existing: a tarball row that already carries an integrity but nohasInstallScriptnever gains it, so its untrusted postinstall is never reported as blocked. Onbun update,install --forceoraddthe tarball is re-read, but with unchanged bytes processDependencyList.rs:183 returns before the new store at :201-210; a git row (:207) and a row with no integrity do gain the flag. Fix: storehas_install_script(and scripts whenscripts.filled) for every re-read tarball row, equal integrity included, or set it where the linkers first record the integrity (PackageInstaller.rs:1574, Installer.rs:293). Such rows come from a plainbun installon an older bun.lock and from a yarn.lock migration (yarn.rs:786). [also at: src/install/PackageManager/processDependencyList.rs:184 - pre-existing: a tarball row whose recorded integrity already equals the file keeps a stalehasInstallScriptand stale bun.lockb scripts, and no command can repair it.; src/install/PackageManager/processDependencyList.rs:206 - pre-existing: a bun.lockb tarball row that an earlier bun already moved keeps its stale scripts andhasInstallScriptforever, so a trusted package keeps running the old build'spostinstallon every install.]Why this was flagged
A bun.lock written before the
hasInstallScriptkey has afile:or URL tarball dependency with a postinstall that is not in trustedDependencies. The user upgrades bun and runs plainbun install: the row is not repinned (PackageManagerDirectories.rs:928-938), the linker records only the integrity (PackageInstaller.rs:1574-1582, isolated Installer.rs:293) and saves the lockfile. Laterbun updatere-reads the tarball, the extracted sha512 equals the recorded one,movedis false at processDependencyList.rs:178 and :183 returnsexistingbefore the new lines :201-210 run, sohasInstallScriptstays unset for good. PackageInstaller.rs:2486 and isolated_install/Installer.rs:1325 gate the blocked-script count on that flag, so the install never prints theBlocked 1 postinstall. Run \bun pm untrusted` for details.line of install_with_manager.rs:1502. The base branch behaves the same; the PR fixes only rows whose integrity differs, and git rows on the same lockfile do get the flag from :207-209 on the samebun update`.Verification: pre-existing. Triggering condition: a bun.lock whose tarball row has an integrity but no
hasInstallScriptkey, followed bybun updatewith unchanged tarball bytes. In processDependencyList.rsif !(from_lockfile && moved) { return existing; }(:182-184) runs before the new store at :206, so the flag is never set; PackageInstaller.rs:2486 gates the blocked-script count on it.
e6b2388 to
4b16aa0
Compare
… of what was read `bun update`, `bun install --force` and `bun add <the same spec>` read a `file:` or URL tarball again. When it held other bytes, its row of the lockfile took the dependencies, the bin and the integrity of the package.json that was read, and kept the lifecycle scripts and `hasInstallScript` that it had. - bun.lockb records the scripts, so a trusted package ran the scripts of the old build in the new one, and `bun pm untrusted` listed them. - bun.lock kept the old `hasInstallScript`, so the install did not report a blocked postinstall that the new build added. The row now takes `hasInstallScript`, and the scripts when it records scripts. A row of bun.lock records none: the installed package.json, which a patch can edit, still decides what runs. The row takes both when the bytes are the same too. That repairs a row that an older version wrote. `bun update` also reads a git or GitHub dependency again at the same commit. Such a row only gets a `hasInstallScript` that it lacks, which is the case for a bun.lock from before the key was written.
4b16aa0 to
12b6213
Compare
There was a problem hiding this comment.
I reviewed the latest push and found no bugs; because it decides which lifecycle scripts a re-read tarball row records and whether an untrusted hook is reported as blocked, a human look is still worthwhile — especially since the title and description still describe a store limited to a moved row, while the code now stores scripts/hasInstallScript for every lockfile tarball row that is read again (same integrity included), which the "Downsides" paragraph lists as not done.
What was reviewed:
append_extracted_packagein src/install/PackageManager/processDependencyList.rs: the new store runs only whenfrom_lockfile, only for tarball rows, and only viarepins_tarball(update / --force / add), which requiressave_lockfile(), so--frozen-lockfileand plainbun installnever reach it;--frozen-lockfilecannot rewrite the row.existing.scriptsis replaced only whenfilled(bun.lockb rows); bun.lock rows keep deferring to the installed, patchable package.json, andpackage.scripts.filledis always true after parse so the row staysfilled.has_install_scripton the re-read package is computed byPackage::parsefor non-root resolvers (scripts or binding.gyp withgypfile), so the copied flag matches what a fresh install writes.- Test helpers:
hostedGitRepository/githubTarballexist in harness with thesha/envshape the tests use; the git helper now drains stdout/stderr/exited concurrently and the edited-checkout case asserts the checkout is read before asserting the flag is kept.
Extended reasoning...
The change is a 17-line edit to append_extracted_package in the package manager plus about 280 lines of tests in the existing tarball-integrity file. It touches the lifecycle-script trust surface: which scripts a bun.lockb row records for a trusted package and whether bun.lock's hasInstallScript gates the "Blocked N postinstall" report, so a wrong value either runs a stale hook or silently hides a new one. The code is small and the guards trace cleanly, but this push changed the semantics from "moved rows only" to "every re-read lockfile tarball row" while the PR title and description still state the narrower behavior, and the author is a bot, so a maintainer should confirm the widened store is the intended design. The prior nits on pipe draining and the edited-checkout precondition are addressed in the code; the optional runtime nit on the debug/ASAN lane is partly mitigated by the new fileOnly skips but not otherwise answered.
Still open from earlier reviews (1):
- Unresolved: 1 minor or pre-existing.
|
On the pre-existing finding of the review (a tarball row that has the integrity and no
Not changed: a plain The three inline nits are addressed in the same commit: the helper reads stdout, the file spawns fewer processes, and the edited-checkout case first shows that The title and the description of the PR now describe this wider store. |
Problem
bun update,bun install --forceandbun add <the same spec>read afile:or URL tarball again. Its lockfile row moves to the new build with the old lifecycle scripts andhasInstallScript.postinstall. With bun.lock the install prints noBlocked 1 postinstall.append_extracted_package(src/install/PackageManager/processDependencyList.rs:198) copiesdependencies,binand the integrity, notscriptsorhas_install_script.Fix
has_install_script, andscriptswhen it records scripts (bun.lockb). The same bytes repair an older row.bun updatereads again only gains a missinghasInstallScript.test/cli/install/bun-install-tarball-integrity.test.ts, 29 cases. Main fails 25. Each clause removed alone fails 1 to 16.resolution).Background
hasInstallScriptgates the count of blocked scripts.scriptsstore. With a patch that editsscripts, 20 of 20 bun.lock cells run the unpatched hook.Downsides
PackageInstaller.rs:1574,isolated_install/Installer.rs:293) still record nothing else.bun updateaddshasInstallScript: trueto tarball, git and GitHub rows of an older bun.lock: one changed line per row.append_extracted_package, +768 bytes of text (release).Notes
Origin. An audit found this, no user reported it. The change replaces commit 1eeca05 of draft #45671, which that PR's self-review asked to have as a PR of its own. #45671 can drop that commit. That commit stored both fields on every row that moves. This one stores them on tarball rows only.
History.
postinstallafterbun update(2 of 2 runs here).bun install --force,bun add <the same spec>and the isolated linker take new bytes too. install: fix regressions and older bugs found after the package manager consolidation #45307 madehasInstallScripta key of bun.lock. No tag contains either commit.npm updateafter the tarball changed movesintegrity, writeshasInstallScript: trueand runs the new hook.What changes, by row. These commands read a tarball again:
bun update,bun update <name>,bun install --force,bun add <the same spec>.file:and URL tarball, bun.lockb: the row records the scripts of what was read. The run and the installs that follow run them.bun pm untrustedlists them.file:and URL tarball, both lockfiles:hasInstallScriptis that of what was read, set or cleared. An install reports a blocked script of the new build.hasInstallScriptgets it. That is a bun.lock from before the key. A row of bun.lockb that an older version moved gets the scripts of its bytes.bun updateaddshasInstallScript: trueto a row that lacks it. The flag is never cleared and the scripts stay. A git row is read from its checkout in the cache. node_modules links to those files on Linux and Windows, so a lifecycle script can have edited them.Designs weighed.
scriptsstore with no condition, which is what a fresh row gets. It saves one read of the installed package.json per moved row of bun.lock. It makesbun updaterun the hook of the tarball where the installed package.json has the patched one: 16 of 16 tarball cells, and 4 of 4bun updatecells of a git or GitHub row with nothing new upstream. main, 1.4.3-canary.1 andbun installrun the patched hook there.replace_packagehelper for this code and the folder resolver. It stores the same row and needs a list of what to keep (resolution, the id, the integrity), so a list stays.Not in this PR.
bun installof a row that has no integrity records the integrity there and nothing else. The next command that reads the tarball again repairs the row.<owner>-<repo>-<7 hex>). On main the archive that was read is discarded when the cache has the folder. install: name a github: cache entry after its commit and the sha512 that bun.lock pins #45671 owns that row.Scripts::get_list,get_installed_package_scripts_count). This PR changes 0 of those cells.Measurements. Debug builds and release builds of main f2de590 and of this PR.
strace,perf,valgrindandbloatyare not installed here, so gdb stands in.append_extracted_package(gdb breakpoints), main -> PR. A registry install (95 packages, warm cache): 0 -> 0. A no-op install: 0 -> 0. A plain install with an unchanged tarball: 0 -> 0.bun updatewith an unchanged tarball: 0 -> 1.bun updateafter the tarball changed: 1 -> 2.append_extracted_package2175 -> 2810 bytes (+635) and 438 -> 566 instructions (+128) (llvm-nm -S,llvm-objdump). Most of it is a second inlined write of the row.sizetext 107,401,036 -> 107,401,804, data and bss equal.node_modules/pkg/package.jsonin a run that reads the tarball again (gdbcatch syscall openat, hoisted and isolated). Trusted: bun.lock 2 -> 2, bun.lockb 1 -> 1. Not trusted, and the row gains the flag: 0 -> 1, the read that counts the blocked script.bun updatewith an unchanged tarball: 0 differing bytes in bun.lock, 0 in bun.lockb.bun updatewith a git dependency and nothing new upstream: 0 and 0. A bun.lock withouthasInstallScript: 1 changed row. A bun.lockb migrated from package-lock.json, after a move: +0 bytes, byte-identical to main's.scripts: 0 of 36 cells change. All run the patched hook on both builds (16 tarball cells with other bytes, 16 with the same bytes, 4bun updatecells of a git or GitHub row). Without a patch: 0 of 36.Tests. 29 cases.
bun updateruns the scripts of the new build when it adds, changes or drops one, with bun.lock and with bun.lockbfile:, both linkersupdate,install --force,add <spec>andupdate pkgrecord the flag and report the blocked script. The builds have a postinstall, a preinstall, a bare binding.gyp, orgypfile: falsefile:and URL, both linkersbun pm untrusted, andbun update --save-text-lockfilefile:and URL, both linkersfile:, both linkersbun updateruns the hook that a patch editedfile:, both linkersfilledcondition 2 fail, thescriptsstore 10 fail, the flag store 16 fail, the store for the same bytes 2 fail, the flag of a git or GitHub row 3 fail, a flag that is also cleared there 1 fails.file:and URL rows, so three of the cases run forfile:only. The two that run for both hold the flag and the scripts of a URL row.Other suites, debug build, 0 fail.
bun-install772,bun-install-lifecycle-scripts428,bun-install-git-deps201,GHSA-pfwx-36v6-832x10,bun-update175,bun-lockb67,bun-patch218,bun-install-patch53,bun-pm154,bun-lock317,bun-update-transitive326,bun-update-lockfile-sync172,hoist175,isolated-install283,bun-add219. Nothing ran here on Windows or macOS.